﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DeepZoomPivotConstructor.VisualLib;
using System.Xml.Serialization;
using DeepZoomPivotConstructor.DTBuilderLib;
using DeepZoomPivotConstructor.ErrorAndLoggingInterfaces;
using DeepZoomPivotConstructor.PivotLib.PivotSchema;
using DeepZoomPivotConstructor.VisualCacheInterfaces;

namespace DeepZoomPivotConstructor.PivotLib
{
    /// <summary>
    /// Allows one to process images (etc.) into a pivot collection.
    /// </summary>
    public class PivotProcessor
    {
        private IErrorLogger Logger { get; set; }

        /// <summary>
        /// Create a new pivot processor
        /// </summary>
        /// <param name="logger"></param>
        public PivotProcessor(IErrorLogger logger, IVisualCacheDB cache = null)
        {
            Logger = logger;
            _cache = cache;
        }

        /// <summary>
        /// The cache we use to store results.
        /// </summary>
        IVisualCacheDB _cache = null;

        /// <summary>
        /// Given a collection and output directory, create the pivot collection,
        /// including running all of the DZ and turning them into a collection.
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public FileInfo CreatePivotCollection(PivotCollection c, DirectoryInfo inf, string collectionName)
        {

            ///
            /// Create the master collection
            /// 

            var pCollection = new PivotSchema.Collection();
            pCollection.Name = collectionName;
            pCollection.SchemaVersion = (decimal) 1.0;

            pCollection.Items = new PivotSchema.itemList[1];
            pCollection.Items[0] = new PivotSchema.itemList();
            VisualHolder masterCollectionVisual = new VisualHolder();
            Dictionary<string, facetType> fTable = new Dictionary<string, facetType>();
            pCollection.Items[0].Item = (from img in c select ConvertToPivotImage(img, masterCollectionVisual, fTable)).ToArray();

            ///
            /// Render the main dude
            /// 

            DirectoryInfo dzImage = new DirectoryInfo(inf.FullName + "\\dz");
            DeepZoomBuilder dzb = new DeepZoomBuilder(Logger, _cache);
            var outputDir = dzb.RenderVisual(dzImage, masterCollectionVisual);
            pCollection.Items[0].ImgBase = "dz\\dz.xml";

            ///
            /// Fix up the numbering...
            /// 

            for (int index = 0; index < pCollection.Items[0].Item.Length; index++)
            {
                pCollection.Items[0].Item[index].Img = "#" + index.ToString();
                pCollection.Items[0].Item[index].Id = index.ToString();
            }

            ///
            /// Do the facet catagories. First this means finding all the catagories we know about!
            /// 

            var facNames = from allitems in pCollection.Items[0].Item
                           from f in allitems.Facets.Facet
                           group f by f.Name;
            var allFacCat = (from fn in facNames select ConvertToFacCategory(c, fn.Key, fTable[fn.Key])).ToArray();

            pCollection.FacetCategories = new PivotSchema.facetCategoryList();
            pCollection.FacetCategories.FacetCategory = allFacCat;
            ///
            /// Write out the file
            /// 

            FileInfo output = new FileInfo(inf.FullName + "\\collection.cxml");
            using (var writer = output.CreateText())
            {
                var ser = new XmlSerializer(typeof(PivotSchema.Collection));
                ser.Serialize(writer, pCollection);
                writer.Close();
            }

            return output;
        }

        /// <summary>
        /// Converts what we know about a facet into a catagory.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="facetName"></param>
        private static PivotSchema.facetCategory ConvertToFacCategory(PivotCollection c, string facetName, facetType fType)
        {
            PivotSchema.facetCategory cat = new PivotSchema.facetCategory();
            var info = c.FacetCatagories[facetName];

            cat.Name = facetName;

            cat.IsFilterVisible = info.IsFilterVisible;
            cat.IsMetaDataVisible = info.IsMetaDataVisible;
            cat.IsWordWheelVisible = info.IsWordWheelVisible;

            cat.IsFilterVisibleSpecified = true;
            cat.IsMetaDataVisibleSpecified = true;
            cat.IsWordWheelVisibleSpecified = true;

            cat.Type = fType;

            return cat;
        }

        /// <summary>
        /// Converts one of our internal image guys into a Pivot image
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        private PivotSchema.item ConvertToPivotImage(PivotImage img, VisualHolder holder, Dictionary<string, facetType> fTable)
        {
            PivotSchema.item result = new PivotSchema.item();

            result.Description = img.Description;
            result.Name = img.Name;

            ///
            /// Add the main image to the list of images we are going to need to run
            /// 

            holder.AddImage(img.Image);

            ///
            /// Do any facets of data that have been added
            /// 

            var byName = from f in img
                         group f by f.Name;
            var facets = (from fs in byName select ConvertToFacet(fs.Key, fs, fTable)).ToArray();
            result.Facets = new PivotSchema.facetList();
            result.Facets.Facet = facets;

            return result;
        }

        /// <summary>
        /// Looks at all the facets registered under a particular name, and constructs
        /// the final results for that facet.
        /// </summary>
        /// <param name="facetName"></param>
        /// <param name="mdList"></param>
        /// <returns></returns>
        private PivotSchema.facet ConvertToFacet(string facetName, IEnumerable<PivotMetadataBase> mdList, Dictionary<string, facetType> fTable)
        {
            PivotSchema.facet result = new PivotSchema.facet();

            result.Name = facetName;
            
            ///
            /// Convert the list, but make sure they are all of the same type
            /// 

            var converted = (from f in mdList select f.ToFacetInfo()).ToArray();
            var typeOfFacet = converted[0].GetType();
            if (!converted.All(f => f.GetType() == typeOfFacet))
            {
                throw new InvalidOperationException("All facet types with same name must be the same type (facet name was '" + facetName + "'");
            }

            ///
            /// Convert to the "right" type...
            /// 

            if (typeOfFacet == typeof(PivotSchema.linkType))
            {
                result.Link = (from f in converted select (PivotSchema.linkType)f).ToArray();
                fTable[facetName] = facetType.Link;
            }
            else if (typeOfFacet == typeof(PivotSchema.stringType))
            {
                result.String = (from f in converted select (PivotSchema.stringType)f).ToArray();
                fTable[facetName] = facetType.String;
            }
            else if (typeOfFacet == typeof(PivotSchema.dateTimeType))
            {
                result.DateTime = (from f in converted select (PivotSchema.dateTimeType)f).ToArray();
                fTable[facetName] = facetType.DateTime;
            }
            else if (typeOfFacet == typeof(PivotSchema.numberType))
            {
                result.Number = (from f in converted select (PivotSchema.numberType)f).ToArray();
                fTable[facetName] = facetType.Number;
            }
            return result;
        }
    }
}
